﻿using Admin.Crypto.Dto;
using Admin.Crypto.Session;
using Admin.Crypto.Utils;
using System;

namespace Admin.Crypto
{
    public class CustomerContactAppService : ApplicationServiceBase, ICustomerContactAppService
    {
        private readonly ICustomerContactRepository repository;
        private readonly IUserSession session;

        public CustomerContactAppService(ICustomerContactRepository repository, IUserSession userSession)
        {
            this.repository = repository;
            this.session = userSession;
        }

        public async Task<ResultOutput<EncryptionOutput>> CreateAsync(string input)
        {
            var data = input;
            if (string.IsNullOrEmpty(data))
            {
                return ResultExtensions.Fail<EncryptionOutput>(400, "待加密信息不能为空");
            }

            if (data.Contains("*"))
            {
                return ResultExtensions.Fail<EncryptionOutput>(400, "不能包含非法字符：*");
            }

            var md5Phone = data.Md5To32Bit();
            var entity = await repository.Context.Queryable<CustomerContact>()
                                          .Where(x => x.TenentId == session.TenantId)
                                          .Where(x => x.Md5Phone == md5Phone)
                                          .FirstAsync();

            var output = new EncryptionOutput();
            if (entity != null)
            {
                output.SemiData = entity.SemiData;
                output.Data = data;
                output.Id = entity.Id;

                return ResultExtensions.Ok(output);
            }

            var id = Guid.NewGuid();
            var encryptPhone = RSAUtils.Encrypt(PublicKey, data);
            var flag = await this.repository.CreateAsync(new CustomerContact
            {
                Id = id,
                SemiData = ReplaceWithSign(data),
                CreateUserId = session.UserId,
                CreationTime = DateTime.Now,
                EncryptionData = encryptPhone,
                Md5Phone = md5Phone,
                TenentId = session.TenantId ?? 0,
            });          

            if (flag)
            {
                output.SemiData = ReplaceWithSign(data);
                output.Data = data;
                output.Id = id;
            }

            return ResultExtensions.Ok(output);
        }

        public async Task<ResultOutput<List<EncryptionOutput>>> CreateListAsync(CreateListInput input)
        {
            if (input.Items != null && input.Items.Any(x => string.IsNullOrEmpty(x)))
            {
                return ResultExtensions.Fail<List<EncryptionOutput>>(400, "批量创建失败，手机号码有空值");
            }

            if (input.Items != null && input.Items.Any(x => x.Contains("*")))
            {
                return ResultExtensions.Fail<List<EncryptionOutput>>(400, "批量创建失败，能包含非法字符：*");
            }

            var md5Phones = input.Items.Select(x => new { 
                Phone = x,
                Md5Phone = x.Md5To32Bit(),
            }).ToList();

            var md5PhoneList = md5Phones.Select(x => x.Md5Phone).ToList();
            var dbContext = repository.Context;

            var existsList = await dbContext.Queryable<CustomerContact>()
                             .Where(x => x.TenentId == session.TenantId)
                             .Where(x => md5PhoneList.Contains(x.Md5Phone))
                             .ToListAsync();

            // 除掉已经存在数据库中的
            var existsMd5Phones = existsList.Select(x => x.Md5Phone).ToList();
            // 需要新增的数据
            var needAddMd5Phones = md5PhoneList.Except(existsMd5Phones);

            var addList = new List<CustomerContact>();
            var encryptions = new List<EncryptionOutput>();
            var rsa = new RSAUtils(PrivateKey, PublicKey);
            foreach (var item in md5Phones)
            {
                if (needAddMd5Phones.Any(x => x == item.Md5Phone))
                {
                    var encryptPhone = rsa.Encrypt(item.Phone);
                    var entity = new CustomerContact
                    {
                        Id = Guid.NewGuid(),
                        SemiData = ReplaceWithSign(item.Phone),
                        CreateUserId = session.UserId,
                        CreationTime = DateTime.Now,
                        EncryptionData = encryptPhone,
                        Md5Phone = item.Md5Phone,
                        TenentId = session.TenantId ?? 0,
                    };

                    addList.Add(entity);

                    var semiPhone = ReplaceWithSign(item.Phone);                  
                    encryptions.Add(new EncryptionOutput
                    {
                        Id = entity.Id,
                        SemiData = semiPhone,
                        Data = item.Phone
                    });
                }
            }

            await dbContext.Insertable(addList).ExecuteCommandAsync();

            foreach (var item in existsList)
            {
                var phone = rsa.Decrypt(item.EncryptionData);
                var semiPhone = ReplaceWithSign(phone);
                encryptions.Add(new EncryptionOutput
                {
                    Id = item.Id,
                    SemiData = semiPhone,
                    Data = phone
                });
            }

            return ResultExtensions.Ok(encryptions);
        }

        public async Task<ResultOutput<DecryptionOutput>> DecryptAsync(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return ResultExtensions.Fail<DecryptionOutput>(400, "请输入id");
            }

            var flag = Guid.TryParse(input, out var guid);
            if (!flag)
            {
                return ResultExtensions.Fail<DecryptionOutput>(400, "非法id");
            }

            var dbContext = repository.Context;

            var entity = await dbContext.Queryable<CustomerContact>()
                             .Where(x => x.Id == guid)
                             .FirstAsync();

            if(entity == null)
            {
                return ResultExtensions.Fail<DecryptionOutput>(400, "没有找到对应记录");
            }

            var phone = RSAUtils.Decrypt(PrivateKey, entity.EncryptionData);
            var output = new DecryptionOutput 
            { 
                SemiData = ReplaceWithSign(phone),
                Data = phone,
                Id = guid
            };

            return ResultExtensions.Ok(output);
        }

        public async Task<ResultOutput<List<DecryptionOutput>>> DecryptListAsync(DecipherListInput input)
        {
            // 开始解密逻辑
            var guids = input.Items;
            var phones = new List<DecryptionOutput>();
            var rsa = new RSAUtils(PrivateKey, PublicKey);

            var dbContext = repository.Context;
            var list = new List<TempPhoneModel>();

            await dbContext.Utilities.PageEachAsync(guids, 500, async ids =>
            {
                var guids = ids.Where(x => !string.IsNullOrEmpty(x) && x.Length == 36).Select(x => Guid.Parse(x)).ToList();

                var rs = await dbContext.Queryable<CustomerContact>()                         
                         .Where(x => guids.Contains(x.Id))
                         .Select(x => new TempPhoneModel
                         {
                             Id = x.Id,
                             EncryptData = x.EncryptionData,
                             Md5Phone = x.Md5Phone
                         })
                         .ToListAsync();

                list.AddRange(rs);
            });

            foreach (var item in list)
            {
                var phone = rsa.Decrypt(item.EncryptData);
                var output = new DecryptionOutput
                {
                    Id = item.Id,
                    SemiData = ReplaceWithSign(phone),
                    Data = phone
                };

                phones.Add(output);
            }

            return ResultExtensions.Ok(phones);
        }

        public async Task<ResultOutput<Guid>> GetIdAsync(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return ResultExtensions.Fail<Guid>(400, "输入参数不能为空");
            }

            var dbContext = repository.Context;
            var md5Phone = input.Md5To32Bit();

            var entity = await dbContext.Queryable<CustomerContact>()
                             .Where(x => x.TenentId == session.TenantId)
                             .Where(x => x.Md5Phone == md5Phone)
                             .FirstAsync();

            if (entity == null)
                return ResultExtensions.Fail<Guid>(400, "根据手机号码搜索，没有找到对应的GUID");

            return ResultExtensions.Ok(entity.Id);
        }

        public async Task<ResultOutput<List<IdListOutput>>> GetIdListAsync(GetIdsInput input)
        {
            var dbContext = repository.Context;

            var md5Phones = input.Items.Select(x => x.Md5To32Bit()).ToList();
            var list = await dbContext.Queryable<CustomerContact>()
                                                    .Where(x => md5Phones.Contains(x.Md5Phone))
                                                    .Where(x => x.TenentId == session.TenantId)
                                                    .ToListAsync();

            if (list == null)
                return ResultExtensions.Fail<List<IdListOutput>>(400, "根据手机号码搜索，没有找到对应的GUID");

            var rsa = new RSAUtils(PrivateKey, PublicKey);
            var output = new List<IdListOutput>();
            foreach (var item in list)
            {
                var dto = new IdListOutput
                {
                    Data = rsa.Decrypt(item.EncryptionData),
                    Id = item.Id
                };
                output.Add(dto);
            }

            return ResultExtensions.Ok(output);
        }
    }   
}
